
;Routines for generting a terminate and stay-resident C program

public       _tsr
extrn        _getpsp:word
HEAPSIZE     equ    64  ;size of heat in 16 byte paragreaphs

_data        segment word public 'DATA'
_data        ends

dgroup       group _data

_text        segment byte public 'CODE'
extrn        _sbrk:near
extrn        _stack_ptr:near
             assume cs:_text,ds:dgroup

;*************************** Code segment data *****************************

c_ss         dw     ?
c_sp         dw     ?
c_ds         dw     ?
c_es         dw     ?

fun_ptr      dw     ?
pre_ptr      dw     ?
int_ptr      dw     ?
hot_key      dw     ?

c_dta_off    dw     ?
c_dta_seg    dw     ?
d_dta_off    dw     ?
d_dta_seg    dw     ?

indos_ptr    label  dword
indos_off    dw     ?
indos_seg    dw     ?

int2b_vec    label  dword
int2b_off    dw     ?
int2b_seg    dw     ?

int2e_vec    label  dword
int2e_off    dw     ?
int2e_seg    dw     ?

int28_vec    label  dword
int28_off    dw     ?
int28_seg    dw     ?

int27_vec    label  dword
int27_off    dw     ?
int27_seg    dw     ?

int26_vec    label  dword
int26_off    dw     ?
int26_seg    dw     ?

int25_vec    label  dword
int25_off    dw     ?
int25_seg    dw     ?

int20_vec    label  dword
int20_off    dw     ?
int20_seg    dw     ?

int13_vec    label  dword
int13_off    dw     ?
int13_seg    dw     ?

int09_vec    label  dword
int09_off    dw     ?
int09_seg    dw     ?

int21_vec    label  dword
int21_off    dw     ?
int21_seg    dw     ?

save_cs      dw     ?
save_ip      dw     ?
save_fl      dw     ?
save_ss      dw     ?
save_sp      dw     ?

break_off    dw     ?
busy         db     0

in_bios      db     0
dos_ss       dw     ?
dos_sp       dw     ?

psp          dw     ?


;************************ Installation procedure ***************************

_tsr         proc near

aframe       struc
abptr        dw     ?
aret_ad      dw     ?
afun_ptr     dw     ?
apre_ptr     dw     ?
aint_ptr     dw     ?
ahot_key     dw     ?
aframe       ends

             push bp
             mov bp,sp
             push es

             mov ah,30h                ;test DOS version
             int 21h
             cmp al,0
             jg a00                    ;2.x or greater
             mov ax,2                  ;version 1.x
             jmp a02
a00:
             mov cs:c_ss,ss            ;save C SS
             mov ax,word ptr _stack_ptr
             mov cs:c_sp,ax            ;save C SP
             mov cs:c_ds,ds
             mov cs:c_es,es

             mov ax,[bp].afun_ptr     ;save pointer to TSR function
             mov cs:fun_ptr,ax
             mov ax,[bp].apre_ptr     ;save pointer to TSR function
             mov cs:pre_ptr,ax
             mov ax,[bp].aint_ptr     ;save pointer to TSR function
             mov cs:int_ptr,ax
             mov ax,[bp].ahot_key     ;save hotkey mask
             mov cs:hot_key,ax

             mov ah,2fh               ;get C DTA
             int 21h
             mov cs:c_dta_off,bx      ;Save it
             mov cs:c_dta_seg,es

             mov ah,34h                ;call DOS to retrieve pointer to
             int 21h                   ; 'indos' flag
             mov cs:indos_off,bx
             mov cs:indos_seg,es

             mov ah,35h                ;get old interrup
             mov al,28h
             int 21h
             mov cs:int28_off,bx       ;save it
             mov cs:int28_seg,es

             mov ah,35h
             mov al,09h
             int 21h
             mov cs:int09_off,bx
             mov cs:int09_seg,es

             mov ah,35h
             mov al,13h
             int 21h
             mov cs:int13_off,bx
             mov cs:int13_seg,es

             mov ah,35h
             mov al,21h
             int 21h
             mov cs:int21_off,bx
             mov cs:int21_seg,es

;**************************** NEW DOS INTERRUPT ****************************

             mov ah,35h
             mov al,20h
             int 21h
             mov cs:int20_off,bx
             mov cs:int20_seg,es

             mov ah,35h
             mov al,25h
             int 21h
             mov cs:int25_off,bx
             mov cs:int25_seg,es

             mov ah,35h
             mov al,26h
             int 21h
             mov cs:int26_off,bx
             mov cs:int26_seg,es

             mov ah,35h
             mov al,27h
             int 21h
             mov cs:int27_off,bx
             mov cs:int27_seg,es

             mov ah,35h
             mov al,2bh
             int 21h
             mov cs:int2b_off,bx
             mov cs:int2b_seg,es

             mov ah,35h
             mov al,2eh
             int 21h
             mov cs:int2e_off,bx
             mov cs:int2e_seg,es

;************************ SET INTERRUPT VECTOR ****************************

             push ds
             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,2bh
             mov dx,offset int2b
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,20h
             mov dx,offset int20
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,25h
             mov dx,offset int25
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,26h
             mov dx,offset int26
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,27h
             mov dx,offset int27
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,2eh
             mov dx,offset int2e
             int 21h

             mov ax,cs                 ;set new interrupt vector
             mov ds,ax
             mov ah,25h
             mov al,28h
             mov dx,offset int28
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,09h
             mov dx,offset int09
             int 21h

             mov ax,cs
             mov ds,ax
             mov ah,25h
             mov al,13h
             mov dx,offset int13
             int 21h

             mov ax,cs                 ;set new interrupt 21h vector
             mov ds,ax
             mov ah,25h
             mov al,21h
             mov dx,offset int21
             int 21h
             pop ds

             mov ax,HEAPSIZE           ;size of heap in 16 byte paragraphs
             mov bl,16                 ;convert to bytes
             mul bl
             push ax
             call _sbrk                ;attempt to move break address
             add sp,2
             mov cs:break_off,ax       ;save break offset address

             cmp ax,-1
             jne a01
             mov ax,1
             jmp a02

a01:
             mov ax,HEAPSIZE            ;restore C heap
             mov bl,16
             mul bl
             neg ax
             push ax
             call _sbrk
             add sp,2

             mov dx,cs:break_off
             mov dx,15
             mov cl,4
             shr dx,cl

             mov ax,ds
             mov dx,ax

             mov ax,_getpsp             ;AX now has segment address of PSP
             mov cs:psp,ax
             sub dx,ax

             add dx,HEAPSIZE            ;leave room for heap

             mov ah,31h                 ;call DOS terminatte and stay resident
             mov al,0
             int 21h
a02:
             pop es
             pop bp
             ret

_tsr         endp

;************************* Interrupt Handlers ******************************

int21        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             call cs:pre_ptr

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int_ret:     mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int21_vec

int21        endp

int2b        proc near
             cmp ax,001fh
             jz ok
             iret
             ;jmp cs:int2b_vec

ok:          mov al,0ffh
             iret
int2b        endp

int2e        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int2e_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             mov dx,2eh
             push dx
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int2e_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int2e_vec

int2e        endp

int27        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int27_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov dx,27h
             push dx
             mov ds,cs:c_ds
             mov es,cs:c_es
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int27_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int27_vec

int27        endp

int26        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int26_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             mov dx,26h
             push dx
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int26_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int26_vec

int26        endp

int25        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int25_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             mov dx,25h
             push dx
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int25_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int25_vec

int25        endp

int20        proc near

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int20_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             mov dx,20h
             push dx
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int20_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             jmp cs:int20_vec

int20        endp

int28        proc     near

             pushf
             call cs:int28_vec

             cli                       ;make sure interrupt are disabled
             cmp cs:busy,0
             je b01
             iret                      ;TSR not busy
b01:
             cmp cs:in_bios,0          ; teaxt BIOS disk activity
             je b02
             iret
b02:
             push ax
             push es
             xor ax,ax
             mov es,ax
             mov ax,word ptr es:[417h] ;read BIOS keyboard
             and ax,cs:hot_key         ;  at 0000:0417
             cmp ax,cs:hot_key
             pop es
             pop ax
             je b03
             iret
b03:
             call activate             ; activate TSR
             iret

int28        endp

int13        proc    far

             mov cs:in_bios,1

             pop cs:save_ip
             pop cs:save_cs

             mov cs:save_sp,sp
             mov cs:save_ss,ss

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             cmp cs:busy,0
             jne int13_ret
             mov cs:busy,1

             sti
             push bp
             push es
             push ds
             push di
             push si
             push dx
             push cx
             push bx
             push ax
             push cs:save_sp
             push cs:save_ss
             push cs:save_ip
             push cs:save_cs

             mov ds,cs:c_ds
             mov es,cs:c_es
             mov dx,13h
             push dx
             call cs:int_ptr
             pop dx

             pop cs:save_cs
             pop cs:save_ip
             pop cs:save_ss
             pop cs:save_sp
             pop ax
             pop bx
             pop cx
             pop dx
             pop si
             pop di
             pop ds
             pop es
             pop bp
             cli

             mov cs:busy,0
int13_ret:   mov sp,cs:save_ss
             mov ss,sp
             mov sp,cs:save_sp

             push cs:save_cs
             push cs:save_ip
             pushf
             call cs:int13_vec

             mov cs:in_bios,0
             ret  2
int13        endp

int09        proc     near

             pushf
             call cs:int09_vec

             cli
             cmp cs:busy,0
             je c01
             iret
c01:
             cmp cs:in_bios,0           ; teaxt BIOS disk activity
             je c02
             iret
c02:
             push ax
             push es
             xor ax,ax
             mov es,ax
             mov ax,word ptr es:[417h] ;read BIOS keyboard
             and ax,cs:hot_key         ;  at 0000:0417
             cmp ax,cs:hot_key
             pop es
             pop ax
             je c03
             iret
c03:
             push ds
             push bx
             lds bx,cs:indos_ptr
             cmp byte ptr ds:[bx],0    ; test 'indos' flag
             pop bx
             pop ds
             je  c04
             iret
c04:
             call activate             ; activate TSR
             iret

int09        endp

activate     proc   near
             mov cs:busy,1

             mov cs:dos_ss,ss
             mov cs:dos_sp,sp

             mov ss,cs:c_ss
             mov sp,cs:c_sp

             push ax
             push bx
             push cx
             push dx
             push bp
             push si
             push di
             push ds
             push es

             mov cx,64                ;save the DOS stack
             mov es,cs:dos_ss
             mov si,cs:dos_sp         ;point ES:SI to top of DOS stack
d01:
             push word ptr es:[si]    ;loop to save 64 words for DOS stack
             inc si
             inc si
             loop d01

             mov ah,2fh               ;save DTA for interrupted program
             int 21h
             mov cs:d_dta_off,bx
             mov cs:d_dta_seg,es

             mov ah,1ah               ;set up new DTA
             mov dx,cs:c_dta_off
             mov ds,cs:c_dta_seg
             int 21h

             mov ds,cs:c_ds
             mov es,cs:c_es

             sti
             call cs:fun_ptr
             cli

             mov ah,1ah               ;restore DTA of intreeupt program
             mov dx,d_dta_off
             mov ds,d_dta_seg
             int 21h

             mov cx,64                ;restore DOS stack
             mov es,cs:dos_ss
             mov si,cs:dos_sp
             add si,128
d02:
             dec si
             dec si
             pop word ptr es:[si]
             loop d02

             pop es                   ;restore machine state
             pop ds
             pop di
             pop si
             pop bp
             pop dx
             pop cx
             pop bx
             pop ax

             mov ss,cs:dos_ss
             mov sp,cs:dos_sp

             mov cs:busy,0            ;reset busy flag
             ret
activate     endp

_text        ends
             end
